#include "ScelConverter.h"
#include <File.h>
#include <Path.h>
#include <ByteOrder.h>
#include <cstring>

// 拼音表偏移
const off_t kPinyinTableOffset = 0x1540;

// 汉语词组表偏移
const off_t kChineseTableOffset = 0x2628;

// 搜狗词库文件头标识
const uint8 kScelHeader[] = {0x40, 0x15, 0x00, 0x00, 0x44, 0x43, 0x53, 0x01, 0x01, 0x00, 0x00, 0x00};

ScelConverter::ScelConverter()
{
}

ScelConverter::~ScelConverter()
{
}

bool ScelConverter::ParseScelFile(const char* filename, DictInfo& info)
{
    BFile file;
    status_t status = file.SetTo(filename, B_READ_ONLY);
    if (status != B_OK)
        return false;

    // 检查文件头
    uint8 header[12];
    if (file.Read(header, sizeof(header)) != sizeof(header))
        return false;

    if (memcmp(header, kScelHeader, sizeof(kScelHeader)) != 0)
        return false;

    // 读取词库信息
    file.Seek(0x130, SEEK_SET);
    info.name = _ReadUTF16String(file, 0x338 - 0x130);
    info.type = _ReadUTF16String(file, 0x540 - 0x338);
    info.description = _ReadUTF16String(file, 0xd40 - 0x540);
    info.example = _ReadUTF16String(file, kPinyinTableOffset - 0xd40);

    // 解析拼音表
    if (!_ParsePinyinTable(file, kPinyinTableOffset, kChineseTableOffset - kPinyinTableOffset))
        return false;

    // 解析中文词组表
    if (!_ParseWordTable(file, kChineseTableOffset))
        return false;

    return true;
}

bool ScelConverter::_ParsePinyinTable(BFile& file, off_t offset, size_t size)
{
    file.Seek(offset, SEEK_SET);

    // 检查拼音表标识
    uint32 pyHeader;
    if (file.Read(&pyHeader, sizeof(pyHeader)) != sizeof(pyHeader))
        return false;

    if (B_LENDIAN_TO_HOST_INT32(pyHeader) != 0x9D010000)
        return false;

    while (file.Position() < offset + size) {
        uint16 index, length;

        if (file.Read(&index, sizeof(index)) != sizeof(index))
            break;
        if (file.Read(&length, sizeof(length)) != sizeof(length))
            break;

        index = B_LENDIAN_TO_HOST_INT16(index);
        length = B_LENDIAN_TO_HOST_INT16(length);

        BString pinyin = _ReadUTF16String(file, length);
        fPinyinTable[index] = pinyin;
    }

    return true;
}

bool ScelConverter::_ParseWordTable(BFile& file, off_t offset)
{
    file.Seek(offset, SEEK_SET);

    while (file.Position() < file.Seek(0, SEEK_END)) {
        uint16 sameCount, pyIndexLen;

        // 读取同音词数量
        if (file.Read(&sameCount, sizeof(sameCount)) != sizeof(sameCount))
            break;
        sameCount = B_LENDIAN_TO_HOST_INT16(sameCount);

        // 读取拼音索引表长度
        if (file.Read(&pyIndexLen, sizeof(pyIndexLen)) != sizeof(pyIndexLen))
            break;
        pyIndexLen = B_LENDIAN_TO_HOST_INT16(pyIndexLen);

        // 读取拼音
        BString pinyin;
        for (uint16 i = 0; i < pyIndexLen / 2; i++) {
            uint16 pyIndex;
            if (file.Read(&pyIndex, sizeof(pyIndex)) != sizeof(pyIndex))
                return false;

            pyIndex = B_LENDIAN_TO_HOST_INT16(pyIndex);
            pinyin += fPinyinTable[pyIndex];
        }

        // 读取词组
        for (uint16 i = 0; i < sameCount; i++) {
            uint16 wordLen;
            if (file.Read(&wordLen, sizeof(wordLen)) != sizeof(wordLen))
                return false;
            wordLen = B_LENDIAN_TO_HOST_INT16(wordLen);

            BString word = _ReadUTF16String(file, wordLen);

            // 读取扩展数据长度
            uint16 extLen;
            if (file.Read(&extLen, sizeof(extLen)) != sizeof(extLen))
                return false;
            extLen = B_LENDIAN_TO_HOST_INT16(extLen);

            // 读取词频
            uint16 frequency;
            if (file.Read(&frequency, sizeof(frequency)) != sizeof(frequency))
                return false;
            frequency = B_LENDIAN_TO_HOST_INT16(frequency);

            // 保存词条
            WordEntry entry = {frequency, pinyin, word};
            fEntries.push_back(entry);

            // 跳过剩余的扩展数据
            file.Seek(file.Position() + extLen - 2, SEEK_SET);
        }
    }

    return true;
}

BString ScelConverter::_ReadUTF16String(BFile& file, size_t length)
{
    BString result;
    std::vector<uint16> buffer(length / 2);

    if (file.Read(buffer.data(), length) != length)
        return result;

    for (size_t i = 0; i < buffer.size(); i++) {
        uint16 ch = B_LENDIAN_TO_HOST_INT16(buffer[i]);
        if (ch == '\r')
            result += '\n';
        else if (ch != ' ')
            result += (char16_t)ch;
    }

    return result;
}

bool ScelConverter::SaveToText(const char* filename) const
{
    BFile file;
    status_t status = file.SetTo(filename, B_CREATE_FILE | B_WRITE_ONLY);
    if (status != B_OK)
        return false;

    for (const auto& entry : fEntries) {
        BString line;
        line.SetToFormat("%d%s %s\n", entry.frequency, entry.pinyin.String(), entry.word.String());
        file.Write(line.String(), line.Length());
    }

    return true;
}

bool ScelConverter::SaveToCin(const char* filename) const
{
    BFile file;
    status_t status = file.SetTo(filename, B_CREATE_FILE | B_WRITE_ONLY);
    if (status != B_OK)
        return false;

    _WriteCinHeader(file);

    for (const auto& entry : fEntries) {
        BString line;
        line.SetToFormat("%s %s\n", entry.pinyin.String(), entry.word.String());
        file.Write(line.String(), line.Length());
    }

    return true;
}

void ScelConverter::_WriteCinHeader(BFile& file) const
{
    const char* header =
        "%gen_inp\n"
        "%ename Sogou-Pinyin\n"
        "%cname 搜狗拼音\n"
        "%encoding UTF-8\n"
        "%selkey 1234567890\n"
        "%keyname begin\n"
        "%keyname end\n";

    file.Write(header, strlen(header));
}